package com.xxdemo.media;

import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.errors.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.UUID;

@Service
public class AnonymousUploadServiceImpl implements AnonymousUploadService {
    private final MinioClient _minioClient;

    public AnonymousUploadServiceImpl(MinioClient minioClient) {
        _minioClient = minioClient;
    }

    @Override
    public Mono<FileUploadChecklist> uploadFileByFileNameAndContent(String origFileName, MediaType contentType, Flux<DataBuffer> dataBufferFlux) {
        String uuidStr = UUID.randomUUID().toString();
        LocalDateTime date = LocalDateTime.now();
        String pathPrefix = String.format("%d-%02d/", date.getYear(), date.getMonthValue());
        String relativeFilePath = pathPrefix + uuidStr + "/" + origFileName.replaceAll("\\p{C}", "");
        String bucket = "rookie-chat";
        // 保留上传记录
        FileUploadChecklist checklist = new FileUploadChecklist();
        checklist.setSn(uuidStr);
        checklist.setBucket(bucket);
        checklist.setFilePath(relativeFilePath);
        checklist.setOrigName(origFileName);
        checklist.setDate(date);
        String contentTypeValue = StringUtils.defaultString(contentType.toString(), "application/octet-stream");
        checklist.setContentType(contentTypeValue);

        // 合并输入流缓冲区(接收数据耗时)
        Mono<InputStream> inputStreamMono = DataBufferUtils.join(dataBufferFlux).map(DataBuffer::asInputStream);
        // 执行单次文件上传操作(发送数据耗时)
        return inputStreamMono.doOnNext((InputStream inputStream) -> {
            uploadToBucket(bucket, relativeFilePath, contentTypeValue, inputStream);
        }).then(Mono.just(checklist));
    }

    private void uploadToBucket(String bucketName, String fileObjectKeyName, String contentTypeValue, InputStream dataStream) {
        PutObjectArgs.Builder argsBuilder = PutObjectArgs.builder();
        argsBuilder.bucket(bucketName);
        argsBuilder.object(fileObjectKeyName);
        argsBuilder.stream(dataStream, -1, 50*1024*1024);
        argsBuilder.contentType(contentTypeValue);
        PutObjectArgs args = argsBuilder.build();
        try {
            _minioClient.putObject(args);
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | NoSuchAlgorithmException | ServerException | XmlParserException e) {
            String whatKind = e.getClass().getName();
            String msg = e.getMessage();
            throw new MediaServerRuntimeException("文件传输失败(MinIO通讯接口报错): " + whatKind + ": " + msg, e);
        } catch (IOException ioException) {
            String msg = ioException.getMessage();
            throw new MediaServerRuntimeException("网络超时导致MinIO文件传输失败: IOException: " + msg, ioException);
        }
    }
}
